
class DBusNotifierBase : public Notifier {
protected:
  static DBusConnection *_connection;

public:
  static bool initialize() {
    bool res = false;
    DBusError err;
    dbus_error_init(&err);
    if ((_connection = dbus_bus_get(DBUS_BUS_SESSION, &err))) {
      res = true;
    } else {
      log::err << "DBus-Notify: failed to get connection";
      if (dbus_error_is_set(&err)) log::err << " - " << err.message;
      log::err << log::endl;
    }
    dbus_error_free(&err);
    return res;
  }
  static void finalize() {
    if (_connection) {
      dbus_connection_unref(_connection);
      _connection = NULL;
    }
  }

private:
  static DBusHandlerResult _on_filter(DBusConnection */*connection*/,
                                      DBusMessage    *message,
                                      void           *user_data) {
    return ((DBusNotifierBase *)user_data)->on_filter(message);
  }

protected:
  virtual DBusHandlerResult on_filter(DBusMessage */*message*/) = 0;

  static bool add_signal_match(const Glib::ustring &interface,
                               const Glib::ustring &signal) {
    boost::format f("type='signal',interface='%1$s',member='%2$s'");
    Glib::ustring match = boost::str(f % interface % signal);
    bool res = true;
    DBusError err;
    dbus_error_init(&err);
    dbus_bus_add_match(_connection, match.c_str(), &err);
    if (dbus_error_is_set(&err)) {
      log::err << "DBus-Notify: failed to add match - "
               << err.message << log::endl;
      res = false;
    }
    dbus_error_free(&err);
    return res;
  }

protected:
  DBusNotifierBase(const Glib::ustring &summary,
                   const Glib::ustring &icon_name,
                   Glib::RefPtr<Gtk::StatusIcon> status_icon) :
    Notifier(summary, icon_name, status_icon)
  {
    if (!dbus_connection_add_filter(_connection,
                                    &DBusNotifierBase::_on_filter,
                                    this, NULL)) {
      log::err << "DBus-Notify: failed to add filter" << log::endl;
    }
  }
};

DBusConnection *DBusNotifierBase::_connection = NULL;

class DBusGalagoNotifier : public DBusNotifierBase {
private:
  static const char *_service;
  static const char *_path;
  static const char *_interface;
  static const char *_signal_closed;

  enum CapType {
    CAP_INVALID     = -1,
    CAP_BODY        = 0,
    CAP_BODY_MARKUP,
    CAP_ICON_SVG,
    CAP_ICON_STATIC,
  };
  struct CapTable {
    CapType     type_;
    const char *name_;
    bool        provided_;
  };
  static CapTable _capabilities[];

  static const char *set_capabilities(DBusMessage *reply) {
    DBusMessageIter iter;
    dbus_message_iter_init(reply, &iter);
    if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
      int cur_type = DBUS_TYPE_INVALID;
      DBusMessageIter subiter;
      dbus_message_iter_recurse(&iter, &subiter);
      while((cur_type = dbus_message_iter_get_arg_type(&subiter)) != DBUS_TYPE_INVALID) {
        if (cur_type == DBUS_TYPE_STRING) {
          const char *val = NULL;
          dbus_message_iter_get_basic(&subiter, &val);
          if (val) {
            for(CapTable *cap = _capabilities; cap->name_; ++cap) {
              if (!strcmp(val, cap->name_)) {
                cap->provided_ = true;
                break;
              }
            }
          }
        }
        dbus_message_iter_next(&subiter);
      }
      if (_capabilities[CAP_ICON_SVG].provided_) {
        log::log << "DBus-Notify: svg supported" << log::endl;
      }
    } else {
      return "invalid value type";
    }
    return NULL;
  }

public:
  static bool initialize() {
    bool res = false;
    DBusMessage *message = NULL, *reply = NULL;
    message = dbus_message_new_method_call(NULL, _path, _interface,
                                           "GetCapabilities");
    if (message) {
      dbus_message_set_auto_start(message, TRUE);
      dbus_message_set_destination(message, _service);

      DBusError err;
      dbus_error_init(&err);
      reply = dbus_connection_send_with_reply_and_block(_connection, message,
                                                        1000, &err);

      const char *err_msg = NULL;
      if (dbus_error_is_set(&err)) {
        err_msg = err.message ? err.message : "ERROR";
      } else if (reply) {
        err_msg = set_capabilities(reply);
      } else {
        err_msg = "invalid reply";
      }
      if (err_msg) {
        log::err << "DBus-Notify: failed to get server capabilities - "
                 << err_msg << log::endl;
      } else {
        if (_capabilities[CAP_BODY].provided_ ||
            _capabilities[CAP_BODY_MARKUP].provided_) {
          if (add_signal_match(_interface, _signal_closed)) res = true;
          res = true;
        } else {
          log::err << "DBus-Notify: body not provided by server"
                   << log::endl;
        }
      }
      dbus_error_free(&err);
      if (reply) dbus_message_unref(reply);
      dbus_message_unref(message);
    } else {
      log::err << "DBus-Notify: failed to initialize message" << log::endl;
    }
    return res;
  }
  static bool initialized() {
    return (_capabilities[CAP_BODY].provided_ ||
            _capabilities[CAP_BODY_MARKUP].provided_);
  }

private:
  int timeout_;
  int urgency_;

  dbus_uint32_t notify_id_;

  void show_notify(dbus_uint32_t replace_id) {
    bool succeeded = false;
    DBusMessage *message = NULL, *reply = NULL;
    message = dbus_message_new_method_call(NULL, _path, _interface, "Notify");
    if (message) {
      const char   *app_name = PACKAGE_NAME;
      const char   *app_icon = "";
      Glib::ustring app_icon_str;
      const char   *summary  = summary_.c_str();
      const char   *body     = message_.c_str();
      dbus_int32_t  timeout  = timeout_;

      if (_capabilities[CAP_ICON_SVG].provided_) {
        app_icon_str = Glib::build_filename(PKGPIXMAPS_SCALABLE_DIR, icon_name_ + ".svg");
        app_icon = app_icon_str.c_str();
      }

      DBusMessageIter iter;
      DBusMessageIter act_ary;
      DBusMessageIter hints_ary;
      dbus_message_iter_init_append(message, &iter);
      dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &app_name);
      dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &replace_id);
      dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &app_icon);
      dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &summary);
      dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &body);
      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &act_ary);
      dbus_message_iter_close_container(&iter, &act_ary);

      char sig[5] = {
        DBUS_DICT_ENTRY_BEGIN_CHAR,
        DBUS_TYPE_STRING,
        DBUS_TYPE_VARIANT,
        DBUS_DICT_ENTRY_END_CHAR,
        '\0'
      };
      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, sig, &hints_ary);
      {
        DBusMessageIter dict;

        dbus_message_iter_open_container(&hints_ary, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
        {
          DBusMessageIter var;
          const char   *key   = "urgency";
          unsigned char value = urgency_;
          dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &key);
          dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, DBUS_TYPE_BYTE_AS_STRING, &var);
          dbus_message_iter_append_basic(&var, DBUS_TYPE_BYTE, &value);
          dbus_message_iter_close_container(&dict, &var);
        }
        dbus_message_iter_close_container(&hints_ary, &dict);

        Glib::RefPtr<Gdk::Pixbuf> icon;
        if (!_capabilities[CAP_ICON_SVG].provided_ &&
            _capabilities[CAP_ICON_STATIC].provided_ &&
            (icon = get_icon())) {
          dbus_message_iter_open_container(&hints_ary, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
          {
            DBusMessageIter var;
            const char *key = "icon_data";
            dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &key);
            dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "(iiibiiay)", &var);
            {
              DBusMessageIter value;
              DBusMessageIter data;
              const char  *pixels    = (const char *)icon->get_pixels();
              dbus_bool_t  has_alpha = icon->get_has_alpha();
              dbus_int32_t width     = icon->get_width();
              dbus_int32_t height    = icon->get_height();
              dbus_int32_t stride    = icon->get_rowstride();
              dbus_int32_t bps       = icon->get_bits_per_sample();
              dbus_int32_t channels  = icon->get_n_channels();
              dbus_int32_t size      = width * height * channels * (bps / 8);
              dbus_message_iter_open_container(&var, DBUS_TYPE_STRUCT, NULL, &value);
              {
                dbus_message_iter_append_basic(&value, DBUS_TYPE_INT32, &width);
                dbus_message_iter_append_basic(&value, DBUS_TYPE_INT32, &height);
                dbus_message_iter_append_basic(&value, DBUS_TYPE_INT32, &stride);
                dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN, &has_alpha);
                dbus_message_iter_append_basic(&value, DBUS_TYPE_INT32, &bps);
                dbus_message_iter_append_basic(&value, DBUS_TYPE_INT32, &channels);
                dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &data);
                dbus_message_iter_append_fixed_array(&data, DBUS_TYPE_BYTE, &pixels, size);
                dbus_message_iter_close_container(&value, &data);
              }
              dbus_message_iter_close_container(&var, &value);
            }
            dbus_message_iter_close_container(&dict, &var);
          }
          dbus_message_iter_close_container(&hints_ary, &dict);
        }

#ifndef NO_ATTACH_NOTIF_SICON
        GdkScreen   *si_screen = NULL;
        GdkRectangle si_rect;
        if (gtk_status_icon_get_geometry(status_icon_->gobj(), &si_screen, &si_rect, NULL)) {
          dbus_message_iter_open_container(&hints_ary, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
          {
            DBusMessageIter var;
            const char   *key   = "x";
            dbus_int32_t  value = si_rect.x + si_rect.width / 2;
            dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &key);
            dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, DBUS_TYPE_INT32_AS_STRING, &var);
            dbus_message_iter_append_basic(&var, DBUS_TYPE_INT32, &value);
            dbus_message_iter_close_container(&dict, &var);
          }
          dbus_message_iter_close_container(&hints_ary, &dict);
          dbus_message_iter_open_container(&hints_ary, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
          {
            DBusMessageIter var;
            const char   *key   = "y";
            dbus_int32_t  value = si_rect.y + si_rect.height / 2;
            dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &key);
            dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, DBUS_TYPE_INT32_AS_STRING, &var);
            dbus_message_iter_append_basic(&var, DBUS_TYPE_INT32, &value);
            dbus_message_iter_close_container(&dict, &var);
          }
          dbus_message_iter_close_container(&hints_ary, &dict);
        }
#endif
      }
      dbus_message_iter_close_container(&iter, &hints_ary);
      dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &timeout);

      dbus_message_set_auto_start(message, TRUE);
      dbus_message_set_destination(message, _service);

      DBusError err;
      dbus_error_init(&err);
      reply = dbus_connection_send_with_reply_and_block(_connection, message, 1000, &err);

      const char *err_msg = NULL;
      if (dbus_error_is_set(&err)) {
        err_msg = err.message ? err.message : "ERROR";
      } else if (reply) {
        DBusMessageIter iter;
        dbus_message_iter_init(reply, &iter);
        if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32) {
          dbus_message_iter_get_basic(&iter, &notify_id_);
          if (notify_id_ != 0) {
            succeeded = true;
          } else {
            err_msg = "invalid id";
          }
        } else {
          err_msg = "invalid return value type";
        }
      } else {
        err_msg = "invalid reply";
      }
      if (err_msg) {
        log::err << "DBus-Notify: failed to show - " << err_msg << log::endl;
      }
      dbus_error_free(&err);
      if (reply) dbus_message_unref(reply);
      dbus_message_unref(message);
    } else {
      log::err << "DBus-Notify: failed to initialize message" << log::endl;
    }
    if (!succeeded) on_close();
  }

protected:
  virtual void on_closed() {
    DBusNotifierBase::on_closed();
    notify_id_ = 0;
  }
  virtual void on_show() {
    if (notify_id_ == 0) show_notify(0);
  }
  virtual void on_close() {
    if (notify_id_) {
      DBusMessage *message = NULL, *reply = NULL;
      message = dbus_message_new_method_call(NULL, _path, _interface,
                                             "CloseNotification");
      if (message) {
        DBusMessageIter iter;
        dbus_message_iter_init_append(message, &iter);
        dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &notify_id_);

        dbus_message_set_auto_start(message, TRUE);
        dbus_message_set_destination(message, _service);

        DBusError err;
        dbus_error_init(&err);
        reply = dbus_connection_send_with_reply_and_block(_connection,
                                                          message, 1000,
                                                          &err);
        if (dbus_error_is_set(&err)) {
          log::err << "DBus-Notify: failed to close - " << err.message
                   << log::endl;
        }
        dbus_error_free(&err);
        if (reply) dbus_message_unref(reply);
        dbus_message_unref(message);
      } else {
        log::err << "DBus-Notify: failed to initialize message" << log::endl;
      }
    }
  }
  virtual void on_set_timeout(int timeout) {
    timeout_ = timeout;
  }
  virtual void on_set_urgency(Urgency urgency) {
    switch(urgency) {
    case URGENCY_LOW:      urgency_ = 0; break;
    case URGENCY_NORMAL:   urgency_ = 1; break;
    case URGENCY_CRITICAL: urgency_ = 2; break;
    }
  }
  virtual void on_update() {
    if (notify_id_) show_notify(notify_id_);
  }

  virtual DBusHandlerResult on_filter(DBusMessage *message) {
    if (dbus_message_is_signal(message, _interface, _signal_closed)) {
      DBusMessageIter iter;
      dbus_message_iter_init(message, &iter);
      if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32) {
        dbus_uint32_t id = 0;
        dbus_message_iter_get_basic(&iter, &id);
        if (id == notify_id_) {
          on_closed();
          return DBUS_HANDLER_RESULT_HANDLED;
        }
      }
    }
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  }

public:
  DBusGalagoNotifier(const Glib::ustring &summary,
                     const Glib::ustring &icon_name,
                     Glib::RefPtr<Gtk::StatusIcon> status_icon) :
    DBusNotifierBase(summary, icon_name, status_icon),
    timeout_        (0),
    urgency_        (0),
    notify_id_      (0)
  {
  }
  ~DBusGalagoNotifier() {
    close();
  }
};

const char *DBusGalagoNotifier::_service   = "org.freedesktop.Notifications";
const char *DBusGalagoNotifier::_path      = "/org/freedesktop/Notifications";
const char *DBusGalagoNotifier::_interface = "org.freedesktop.Notifications";
const char *DBusGalagoNotifier::_signal_closed = "NotificationClosed";

DBusGalagoNotifier::CapTable DBusGalagoNotifier::_capabilities[] = {
  {CAP_BODY,        "body",          false},
  {CAP_BODY_MARKUP, "body-markup",   false},
  {CAP_ICON_SVG,    "image/svg+xml", false},
  {CAP_ICON_STATIC, "icon-static",   false},
  {CAP_INVALID,     NULL,            false}
};

class DBusKNotifyNotifier : public virtual DBusNotifierBase {
private:
  static const char *_service;
  static const char *_path;
  static const char *_interface;
  static const char *_signal_closed;

public:
  static void initialize() {
    add_signal_match(_interface, _signal_closed);
  }

private:
  Glib::ustring event_;

  dbus_int32_t notify_id_;

protected:
  virtual void on_closed() {
    DBusNotifierBase::on_closed();
    notify_id_ = 0;
  }
  virtual void on_show() {
    DBusMessage *message = NULL, *reply = NULL;
    message = dbus_message_new_method_call(NULL, _path, _interface, "event");
    if (message) {
      const char   *event    = "warning";
      const char   *from_app = "kde";
      const char   *title    = summary_.c_str();
      const char   *text     = message_.c_str();
      dbus_int64_t  winid    = 0;

      DBusMessageIter iter;
      DBusMessageIter context_ary;
      DBusMessageIter pixmap_ary;
      DBusMessageIter action_ary;
      dbus_message_iter_init_append(message, &iter);
      dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &event);
      dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &from_app);
      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_VARIANT_AS_STRING, &context_ary);
      dbus_message_iter_close_container(&iter, &context_ary);
      dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &title);
      dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &text);
      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &pixmap_ary);
      dbus_message_iter_close_container(&iter, &pixmap_ary);
      dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &action_ary);
      dbus_message_iter_close_container(&iter, &action_ary);
      dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT64, &winid);

      dbus_message_set_auto_start(message, TRUE);
      dbus_message_set_destination(message, _service);

      DBusError err;
      dbus_error_init(&err);
      reply = dbus_connection_send_with_reply_and_block(_connection, message, 1000, &err);

      const char *err_msg = NULL;
      if (dbus_error_is_set(&err)) {
        err_msg = err.message ? err.message : "ERROR";
      } else if (reply) {
        DBusMessageIter iter;
        dbus_message_iter_init(reply, &iter);
        if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INT32) {
          dbus_message_iter_get_basic(&iter, &notify_id_);
          if (notify_id_ == 0) on_close();
        } else {
          err_msg = "invalid return value";
        }
      } else {
        err_msg = "invalid reply";
      }
      if (err_msg) {
        log::err << "DBus-Notify: failed to show - " << err_msg << log::endl;
      }
      dbus_error_free(&err);
      if (reply) dbus_message_unref(reply);
      dbus_message_unref(message);
    } else {
      log::err << "DBus-Notify: failed to initialize message" << log::endl;
    }
  }
  virtual void on_close() {
    if (notify_id_) {
      DBusMessage *message = NULL;
      message = dbus_message_new_method_call(NULL, _path, _interface,
                                             "closeNotification");
      if (message) {
        DBusMessageIter iter;
        dbus_message_iter_init_append(message, &iter);
        dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &notify_id_);

        dbus_message_set_auto_start(message, TRUE);
        dbus_message_set_destination(message, _service);

        if (!dbus_connection_send(_connection, message, NULL)) {
          log::err << "DBus-Notify: failed to close" << log::endl;
        }
        dbus_message_unref(message);
      } else {
        log::err << "DBus-Notify: failed to initialize message" << log::endl;
      }
    }
  }
  virtual void on_set_timeout(int /*timeout*/) {
  }
  virtual void on_set_urgency(Urgency /*urgency*/) {
  }
  virtual void on_update() {
    if (notify_id_) {
      DBusMessage *message = NULL;
      message = dbus_message_new_method_call(NULL, _path, _interface, "update");
      if (message) {
        dbus_int32_t  id    = notify_id_;
        const char   *title = summary_.c_str();
        const char   *text  = message_.c_str();
        unsigned char pixmap[4] = {0, 0, 0, 0};

        DBusMessageIter iter;
        DBusMessageIter pixmap_ary;
        DBusMessageIter action_ary;
        dbus_message_iter_init_append(message, &iter);
        dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &id);
        dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &title);
        dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &text);
        dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &pixmap_ary);
        dbus_message_iter_append_basic(&pixmap_ary, DBUS_TYPE_BYTE, pixmap);
        dbus_message_iter_append_basic(&pixmap_ary, DBUS_TYPE_BYTE, pixmap + 1);
        dbus_message_iter_append_basic(&pixmap_ary, DBUS_TYPE_BYTE, pixmap + 2);
        dbus_message_iter_append_basic(&pixmap_ary, DBUS_TYPE_BYTE, pixmap + 3);
        dbus_message_iter_close_container(&iter, &pixmap_ary);
        dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &action_ary);
        dbus_message_iter_close_container(&iter, &action_ary);

        dbus_message_set_auto_start(message, TRUE);
        dbus_message_set_destination(message, _service);

        if (!dbus_connection_send(_connection, message, NULL)) {
          log::err << "DBus-Notify: failed to update" << log::endl;
        }
        dbus_message_unref(message);
      } else {
        log::err << "DBus-Notify: failed to initialize message" << log::endl;
      }
    }
  }

  virtual DBusHandlerResult on_filter(DBusMessage *message) {
    if (dbus_message_is_signal(message, _interface, _signal_closed)) {
      DBusMessageIter iter;
      dbus_message_iter_init(message, &iter);
      log::log << dbus_message_iter_get_arg_type(&iter) << ":"
               << DBUS_TYPE_INT32 << log::endl;
      if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INT32) {
        dbus_int32_t id = 0;
        dbus_message_iter_get_basic(&iter, &id);
        log::log << "on_filter" << id << log::endl;
        if (id == notify_id_) {
          on_closed();
          return DBUS_HANDLER_RESULT_HANDLED;
        }
      }
    }
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  }

protected:
  DBusKNotifyNotifier(const Glib::ustring &event,
                      const Glib::ustring &summary,
                      const Glib::ustring &icon_name,
                      Glib::RefPtr<Gtk::StatusIcon> status_icon) :
    DBusNotifierBase(summary, icon_name, status_icon),
    event_          (event),
    notify_id_      (0)
  {
  }
  ~DBusKNotifyNotifier() {
    close();
  }
};

const char *DBusKNotifyNotifier::_service   = "org.kde.knotify";
const char *DBusKNotifyNotifier::_path      = "/Notify";
const char *DBusKNotifyNotifier::_interface = "org.kde.KNotify";
const char *DBusKNotifyNotifier::_signal_closed = "notificationClosed";

class DBusNotifier : public DBusGalagoNotifier,
                     public DBusKNotifyNotifier {
private:
  RefAttributeNotifyBackend backend_;
public:
  inline RefAttributeNotifyBackend backend() {
    return backend_;
  }

protected:
  virtual void on_closed() {
    DBusGalagoNotifier::on_closed();
    DBusKNotifyNotifier::on_closed();
  }
  virtual void on_show() {
    switch(*backend_) {
    case NOTIFY_BACKEND_GALAGO:
      if (DBusGalagoNotifier::initialized()) {
        DBusGalagoNotifier::on_show();
        break;
      }
    case NOTIFY_BACKEND_KNOTIFY:
      DBusKNotifyNotifier::on_show();
      break;
    }
  }
  virtual void on_close() {
    DBusGalagoNotifier::on_close();
    DBusKNotifyNotifier::on_close();
  }
  virtual void on_set_timeout(int timeout) {
    DBusGalagoNotifier::on_set_timeout(timeout);
    DBusKNotifyNotifier::on_set_timeout(timeout);
  }
  virtual void on_set_urgency(Urgency urgency) {
    DBusGalagoNotifier::on_set_urgency(urgency);
    DBusKNotifyNotifier::on_set_urgency(urgency);
  }
  virtual void on_update() {
    switch(*backend_) {
    case NOTIFY_BACKEND_GALAGO:
      if (DBusGalagoNotifier::initialized()) {
        DBusGalagoNotifier::on_update();
        break;
      }
    case NOTIFY_BACKEND_KNOTIFY:
      DBusKNotifyNotifier::on_update();
      break;
    }
  }

  virtual DBusHandlerResult on_filter(DBusMessage *message) {
    if (DBusGalagoNotifier::on_filter(message) == DBUS_HANDLER_RESULT_HANDLED) {
      return DBUS_HANDLER_RESULT_HANDLED;
    }
    if (DBusKNotifyNotifier::on_filter(message) == DBUS_HANDLER_RESULT_HANDLED) {
      return DBUS_HANDLER_RESULT_HANDLED;
    }
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  }

private:
  void backend_on_changed() {
    on_close();
  }

public:
  DBusNotifier(const Glib::ustring &event,
               const Glib::ustring &summary,
               const Glib::ustring &icon_name,
               Glib::RefPtr<Gtk::StatusIcon> status_icon,
               RefAttributeNotifyBackend backend) :
    DBusNotifierBase   (summary, icon_name, status_icon),
    DBusGalagoNotifier (summary, icon_name, status_icon),
    DBusKNotifyNotifier(event, summary, icon_name, status_icon),
    backend_           (backend)
  {
    backend_->signal_changed()
      .connect(sigc::mem_fun(*this, &DBusNotifier::backend_on_changed));
  }
  ~DBusNotifier() {
    close();
  }
};
